package main;

import java.awt.AWTException;
import java.awt.Dimension;
import java.awt.MouseInfo;
import java.awt.Point;
import java.awt.Robot;
import java.awt.Toolkit;
import java.awt.event.InputEvent;
import java.util.ArrayList;

import com.googlecode.javacv.FrameGrabber.Exception;
import com.googlecode.javacv.OpenCVFrameGrabber;
import com.googlecode.javacv.cpp.opencv_core.CvPoint;
import com.googlecode.javacv.cpp.opencv_core.IplImage;

public class HandDetector {

    public static void main(String[] args) {

	Robot robot = null;
	try {
	    robot = new Robot();
	} catch (AWTException e) {
	    e.printStackTrace();
	}

	OpenCVFrameGrabber grabber = new OpenCVFrameGrabber(0);
	FingerExtractor handExtractor = new FingerExtractor(grabber,
		CONSTANTS.DETECTION_METHOD_CONTOUR);

	// get screen dimensions
	Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
	double screenWidth = screenSize.getWidth(); // for easier access to
						    // screen edges
	double screenHeight = screenSize.getHeight();

	// get camera image dimensions
	double imageWidth = 0;
	double imageHeigth = 0;
	try {
	    IplImage cameraImage = grabber.grab();
	    imageWidth = cameraImage.width();
	    imageHeigth = cameraImage.height();
	} catch (Exception e1) {
	    e1.printStackTrace();
	}

	// calculate the ratio
	double widthRatio = screenWidth / imageWidth;
	double heightRatio = screenHeight / imageHeigth;

	// run handExtractor
	handExtractor.start();

	// declare some useful variables
	ArrayList<CvPoint> currentFingertips, previousFingertips = null;
	int numberOfCurrentFingertips, numberOfPreviousFingertips = 0;
	Point currentCursorPosition = MouseInfo.getPointerInfo().getLocation();
	Point desiredCursorPosition;
	// Point previousMiddle = new Point();
	boolean pressedLeft = false;

	// move the cursor
	while (true) {
	    try {
		Thread.sleep(5);
	    } catch (InterruptedException e) {
		// TODO Auto-generated catch block
		e.printStackTrace();
	    }
	    currentFingertips = handExtractor.getFingertips();
	    numberOfCurrentFingertips = currentFingertips.size();

	    if (numberOfCurrentFingertips > 0) {
		if (numberOfPreviousFingertips == 2
			&& numberOfCurrentFingertips == 1) {
		    int leftTipIndex = previousFingertips.get(0).x() > previousFingertips
			    .get(1).x() ? 0 : 1;
		    if (distanceBetween(previousFingertips.get(leftTipIndex),
			    currentFingertips.get(0)) > distanceBetween(
			    previousFingertips.get((leftTipIndex + 1) % 2),
			    currentFingertips.get(0))) {
			System.out.println("Right down");
			robot.mousePress(InputEvent.BUTTON3_DOWN_MASK);
			pressedLeft = false;
		    } else {
			System.out.println("Left down");
			robot.mousePress(InputEvent.BUTTON1_DOWN_MASK);
			pressedLeft = true;
		    }
		} else if (numberOfPreviousFingertips == 1
			&& numberOfCurrentFingertips == 2) {
		    if (pressedLeft) {
			System.out.println("Left up");
			robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK);
		    } else {
			System.out.println("Right up");
			robot.mouseRelease(InputEvent.BUTTON3_DOWN_MASK);
		    }
		} else {
		    desiredCursorPosition = findTheMiddle(currentFingertips,
			    numberOfCurrentFingertips);

		    // for smooth cursor moving
		    currentCursorPosition.x += Math
			    .signum(desiredCursorPosition.x * widthRatio
				    - currentCursorPosition.x);
		    currentCursorPosition.y += Math
			    .signum(desiredCursorPosition.y * heightRatio
				    - currentCursorPosition.y);

		    robot.mouseMove((int) (currentCursorPosition.x),
			    (int) (currentCursorPosition.y));
		    // previousMiddle = desiredCursorPosition;
		}

		previousFingertips = currentFingertips;
		numberOfPreviousFingertips = numberOfCurrentFingertips;
	    }

	    // handExtractor.getFoundContourType();
	}
    }

    private static Point findTheMiddle(ArrayList<CvPoint> points,
	    int numberOfPoints) { // throws ArithmeticException // by zero
	int sumX = 0, sumY = 0;
	for (CvPoint point : points) {
	    sumX += point.x() - 20;
	    sumY += point.y() - 20;
	}
	return new Point(sumX / numberOfPoints, sumY / numberOfPoints);
    }

    private static double distanceBetween(CvPoint p1, CvPoint p2) {
	return Math.sqrt((p1.x() - p2.x()) * (p1.x() - p2.x())
		+ (p1.y() - p2.y()) * (p1.y() - p2.y()));
    }
}